/***************************************************************************
 *   Copyright (C) 2008 by Łukasz Spintzyk   *
 *   lukasz.spintzyk@gmail.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "lcdcontrol.h"


// zmienne przechowyj�ce aktualne po�o�enie kursora
unsigned char lcd_x, lcd_y;
unsigned char pol = 1;
 
// funkcja op��niaj�ca
void delay2(void)
{

    asm("nop");asm("nop");
}

void lcdInit(void)
{
    LCD_DATA_DDR = 0xFF;
    LCD_CTRL_DDR |= ((1 << LCD_CS1P) | (1 << LCD_CS2P) | 
            (1 << LCD_RS) | (1 << LCD_RW) | (1 << LCD_EN));
}
// funkcja sprawdzaj�ca zaj�to�� kontrolera wy�wietlacza
void lcdWait(void)
{
    LCD_DATA_DDR = 0x00; // ustawienie portu danych  w tryb wej�ciowy 
    CLR_RS(); // niski stan na linii RS -> rejestr statusu
    SET_RW(); // wysoki stan na linii RW -> odczyt z wy�wietlacza
    do { //p�tla
        delay(); // op��nienie
        SET_EN();  // ustawienie lini� EN
        delay(); // op��nienie
        CLR_EN();  // wyzeruj lini� EN
    } while((LCD_DATA_PIN & DISPLAY_STATUS_BUSY)); // powtarzaj do wyzerowania flagi BUSY

}

void lcdWriteCmd(unsigned char cmd)
{
    lcdWait(); // oczekiwanie na gotowo�� kontrolera
    CLR_RS(); // niski stan na linii RS -> rozkaz
    CLR_RW(); // niski stan na linii RW -> zapis
    LCD_DATA_DDR = 0xFF; // port danych -> wyj�cie
    LCD_DATA_PORT = cmd; // wystawienie na port instrucji
    SET_EN(); // ustawienie linii EN
    delay(); // op��nienie
    CLR_EN(); // wyzerowanie linii EN
}

void lcdWriteData(unsigned char data)
{
    if(lcd_x < 64) // je�li wsp��rz�dna x wy�wietlacza < 64
    {LCD_CS0()}  // to zapisujemy do pierwszego kontrolera
    else 		   // w przeciwnym razie	
    {LCD_CS1()}  // zapisujemy do drugiego kontrolera
    lcdWait();  // oczekiwanie na gotowo�� kontrolera
    SET_RS();   // wysoki stan na linii RS -> dane
    CLR_RW();   // niski stan na linii RW -> zapis
    LCD_DATA_DDR = 0xFF; // port danych -> wyj�cie
    LCD_DATA_PORT = data; // wystawienie na port danej
    SET_EN(); // wysoki stan na linii EN
    delay(); // op��nienie
    CLR_EN(); // niski stan na linii EN
    lcd_x++; // zwi�kszenie wsp��rz�dnej x wy�wietlacza (pomocniczej)
    if(lcd_x > 127) // jesli koniec ekranu
        lcd_x = 0; // to wyzeruj wsp��rz�dn� x
    LCD_NOCS();
}

unsigned char lcdReadData(void)
{
    unsigned char data;
    if(lcd_x < 64)  // je�li wsp��rz�dna x wy�wietlacza < 64
    {LCD_CS0()}   // to odczytujemy z pierwszego kontrolera
    else 			// w przeciwnym razie
    {LCD_CS1()}   // odczytujemy z drugiego kontrolera
    lcdWait(); // oczekiwanie na gotowo�� kontrolera
    SET_RS();  // wysoki stan na linii RS -> dane
    SET_RW();  // wysoki stan na linii RW -> odczyt
    SET_EN();  // wysoki stan na linii EN
    delay();   // op��nienie
    LCD_DATA_DDR = 0x00; // ustawienie portu danych w tryb wejsciowy
    data = LCD_DATA_PIN; // odczyt danych z portu
    CLR_EN();  // niski stan na linii EN
    lcd_x++; // zwi�kszenie wsp��rz�dnej x wy�wietlacza
    if(lcd_x > 127) // jesli koniec ekranu
        lcd_x = 0; // to wyzeruj wsp��rz�dn� x
    LCD_NOCS();
    return data;
}


void lcdOn(void)
{
    LCD_CS0(); // aktywny pierwszy kontroler
    lcdWriteCmd(DISPLAY_ON_CMD | ON); // w��czenie wy�wietlania danych
    LCD_CS1(); // aktywny drugi kontroler
    lcdWriteCmd(DISPLAY_ON_CMD | ON); // w��czenie wy�wietlania danych
    LCD_NOCS(); // 
}

void lcdOff(void)
{
    LCD_CS0(); // aktywny pierwszy kontroler
    lcdWriteCmd(DISPLAY_ON_CMD | OFF); // wy��czenie wy�wietlania danych
    LCD_CS1(); // aktywny drugi kontroler
    lcdWriteCmd(DISPLAY_ON_CMD | OFF); // wy��czenie wy�wietlania danych
    LCD_NOCS(); //
}

void lcdCls(unsigned char pattern)
{
    unsigned char x, y; // pomocnicze zmienne
    for (y = 0; y < 8; y++) // 8-krotne powt�rzenie petli
    {
        lcdGoTo(0,y); // ustawienie wsp��rz�dnej y wy�wietlacza
        for (x = 0; x < 128; x++) // 128-krotne powt�rzenie p�tli
            lcdWriteData(pattern); // zapis do pami�ci wy�wietlacza wzorca
    }
    lcdGoTo(0,0); // ustawienie pocz�tkowych wsp��rz�dnych
}

void lcdWriteChar(char x)
{
    char i; // zmienna pomocnicza
    x -= 32; // konwersja kodu
    for(i = 0; i < 5; i++) // pi�ciokrotne wykonanie p�tli
        lcdWriteData(pgm_read_byte(font5x7 + (5 * x) + i)); // zapis sk�adowej znaku
    lcdWriteData(0x00); // odst�p mi�dzy znakami
}

void lcdWriteCharNeg(char x)
{
    char i; // zmienna pomocnicza
    x -= 32; // konwersja kodu
    for(i = 0; i < 5; i++) // pi�ciokrotne wykonanie p�tli
        lcdWriteData(~pgm_read_byte(font5x7 + (5 * x) + i)); // zapis sk�adowej znaku
    lcdWriteData(0xFF); // odst�p mi�dzy znakami
}

void lcdWriteString(char * s)
{
    while(*s)
    {
        lcdWriteChar(*s++);
    }
}

void lcdWriteStringPgm(prog_char * s)
{
    char c;
    char posy = 0;
    while((c = pgm_read_byte(s++)))
    {
        lcdWriteChar(c);
        if(lcd_x >= 126)
            lcdLocate(0, ++posy);
    }
}

void lcdWriteStringPgmNeg(prog_char * s)
{
    char c;
    char posy = 0;
    while((c = pgm_read_byte(s++)))
    {
        lcdWriteCharNeg(c);
        if(lcd_x >= 126)
            lcdLocate(0, ++posy);
    }
}

void lcdLocate(unsigned char x, unsigned char y)
{
    lcdGoTo(x * 6, y); // zapis 
}

void lcdGoTo(unsigned char x, unsigned char y)
{
    lcd_x = x; // przypisanie wsp��rz�dym globalnym nowych warto�ci
    lcd_y = y;
    if(lcd_x > 63) // je�li wsp��rz�dna pozioma jest wi�ksza od 64 to
    {            
        LCD_CS1(); // uaktywnienie drugiego kontrolera
        lcdWriteCmd(DISPLAY_SET_X | lcd_y); // zapis wsp��rz�dnej pionowej
        lcdWriteCmd(DISPLAY_SET_Y | (lcd_x - 64)); // zapis wsp��rz�dnej poziomej
    }
    else // w przeciwnym razie
    {
        LCD_CS0(); // uatywnienie pierwszego kontrolera
        lcdWriteCmd(DISPLAY_SET_X | lcd_y); // zapis wsp��rz�dnej pionowej
        lcdWriteCmd(DISPLAY_SET_Y | lcd_x); // zapis wsp��rz�dnej poziomej
        LCD_CS1(); // uaktywnienie drugiego kontrolera
        lcdWriteCmd(DISPLAY_SET_X | lcd_y); // zapis wsp��rz�dnej pionowej
        lcdWriteCmd(DISPLAY_SET_Y | 0 ); // wyzerowanie wsp��rz�dnej poziomej
    }
    LCD_CS0(); // uaktywnienie pierwszego kontrolera
    lcdWriteCmd(DISPLAY_START_LINE | 0); // 
    LCD_CS1(); // uaktywnienie drugiego kontrolera
    lcdWriteCmd(DISPLAY_START_LINE | 0);
    LCD_NOCS();
}


void lcdBitmap(char * bmp, unsigned char x, unsigned char y, unsigned char dx, unsigned char dy)
{
    unsigned char i, j;
// p�tlawykona si� tyle razy, ile stron zajmuje bitmapa
    for(j = 0; j < dy / 8; j++)
    {
  // ustawienie wsp��rz�dnych
        lcdGoTo(x,y + j);
  // p�tla wykona si� tyle razy, ile pikseli szeroko�ci ma bitmapa
        for(i = 0; i < dx; i++)
        {
            lcdWriteData(pgm_read_byte(bmp++)); // zapis danych
        }
    }
}



void lcdSetPixel(unsigned char x, unsigned char y)
{
    unsigned char temp;  // zmienna pomocnicza
    lcdGoTo(x, y/8); // ustawienie wsp��rz�dnych
    temp = lcdReadData(); // 
    lcdGoTo(x, y/8); // ustawienie wsp��rz�dnych
    temp = lcdReadData(); // odczyt aktualnego stanu pikseli
    lcdGoTo(x, y/8); // ponowne ustawienie wsp��rz�dnych
    lcdWriteData(temp | (1 << (y % 8))); // zapis odpowiedno zmodyfikowanej warto�ci
}

void lcdClrPixel(unsigned char x, unsigned char y)
{
    unsigned char temp; // zmienna pomocnicza
    lcdGoTo(x, y/8); // ustawienie wsp��rz�dnych
    temp = lcdReadData(); // 
    lcdGoTo(x, y/8); // ustawienie wsp��rz�dnych
    temp = lcdReadData(); // odczyt aktualnego stanu pikseli
    lcdGoTo(x, y/8); // ponowne ustawienie wsp��rz�dnych
    lcdWriteData(temp & ~(1 << (y % 8))); // zapis odpowiednio zmodyfikowanej warto�ci
}

void lcdRectangle(unsigned char x, unsigned char y, unsigned char b, unsigned char a)
{
    unsigned char j; // zmienna pomocnicza
  // rysowanie linii pionowych (boki)
    for (j = 0; j < a; j++) {
        lcdSetPixel(x, y + j);
        lcdSetPixel(x + b - 1, y + j);
    }
  // rysowanie linii poziomych (podstawy)
    for (j = 0; j < b; j++)	{
        lcdSetPixel(x + j, y);
        lcdSetPixel(x + j, y + a - 1);
    }
}

void lcdFillRectangle(unsigned char x, unsigned char y, unsigned char b, unsigned char a)
{
    unsigned char i, j;
// ilo�� stron zaj�tych przez prostok�t
    unsigned char div8 = a / 8;
// ilo�� pozosta�ych linii (nie tworz�cych pe�nej strony)
    unsigned char mod8 = a % 8;
// p�tla wykona si� tyle razy, ile stron zajmuje prostok�t
    for(j = 0; j < div8; j++)
    {
        lcdGoTo(x,j); // ustawienie wsp��rz�dnych
  // p�tla wykona si� tyle razy, ile punkt�w jest w linii
        for(i = 0; i <= b; i++) 
        {
            lcdWriteData(0xFF); // zapis strony
        }
    }
// dorysowujemy pozosta�e linie
    for(i = 0; i <= mod8; i++)
    {
  // p�tla wykona si� tyle razy, ile punkt�w jest w linii
        for(j = 0; j <= b; j++)
            lcdSetPixel(x + j, y + i + (a - mod8));
    }
}


void lcdCircle(unsigned char cx, unsigned char cy ,unsigned char radius)
{
    int x, y, xchange, ychange, radiusError;
    x = radius;
    y = 0;
    xchange = 1 - 2 * radius;
    ychange = 1;
    radiusError = 0;
    while(x >= y)
    {
        lcdSetPixel(cx+x, cy+y); 
        lcdSetPixel(cx-x, cy+y); 
        lcdSetPixel(cx-x, cy-y);
        lcdSetPixel(cx+x, cy-y); 
        lcdSetPixel(cx+y, cy+x); 
        lcdSetPixel(cx-y, cy+x); 
        lcdSetPixel(cx-y, cy-x); 
        lcdSetPixel(cx+y, cy-x); 
        y++;
        radiusError += ychange;
        ychange += 2;
        if ( 2*radiusError + xchange > 0 )
        {
            x--;
            radiusError += xchange;
            xchange += 2;
        }
    }
}


void lcdLine(int X1, int Y1,int X2,int Y2)
{
    int CurrentX, CurrentY, Xinc, Yinc, 
    Dx, Dy, TwoDx, TwoDy, 
    TwoDxAccumulatedError, TwoDyAccumulatedError;

    Dx = (X2-X1); // obliczenie sk�adowej poziomej
    Dy = (Y2-Y1); // obliczenie sk�adowej pionowej

    TwoDx = Dx + Dx; // podwojona sk�adowa pozioma
    TwoDy = Dy + Dy; // podwojona sk�adowa pionowa

    CurrentX = X1; // zaczynamy od X1
    CurrentY = Y1; // oraz Y1

    Xinc = 1; // ustalamy krok zwi�kszania pozycji w poziomie 
    Yinc = 1; // ustalamy krok zwi�kszania pozycji w pionie

    if(Dx < 0) // jesli sk�adowa pozioma jest ujemna 
    {
        Xinc = -1; // to b�dziemy si� "cofa�" (krok ujemny)
        Dx = -Dx;  // zmieniamy znak sk�adowej na dodatni
        TwoDx = -TwoDx; // jak r�wnie� podwojonej sk�adowej
    }

    if (Dy < 0) // je�li sk�adowa pionowa jest ujemna
    {
        Yinc = -1; // to b�dziemy si� "cofa�" (krok ujemny)
        Dy = -Dy; // zmieniamy znak sk�adowej na dodatki
        TwoDy = -TwoDy; // jak r�wniez podwojonej sk�adowej
    }

    lcdSetPixel(X1,Y1); // stawiamy pierwszy krok (zapalamy pierwszy piksel)

    if ((Dx != 0) || (Dy != 0)) // sprawdzamy czy linia sk�ada si� z wi�cej ni� jednego punktu ;)
    {
  // sprawdzamy czy sk�adowa pionowa jest mniejsza lub r�wna sk�adowej poziomej
        if (Dy <= Dx) // je�li tak, to idziemy "po iksach"
        { 
            TwoDxAccumulatedError = 0; // zerujemy zmienn� 
            do // ruszamy w drog�
            {
                CurrentX += Xinc; // do aktualnej pozycji dodajemy krok 
                TwoDxAccumulatedError += TwoDy; // a tu dodajemy podwojon� sk�adow� pionow�
                if(TwoDxAccumulatedError > Dx)  // je�li TwoDxAccumulatedError jest wi�kszy od Dx
                {
                    CurrentY += Yinc; // zwi�kszamy aktualn� pozycj� w pionie
                    TwoDxAccumulatedError -= TwoDx; // i odejmujemy TwoDx
                }
                lcdSetPixel(CurrentX,CurrentY);// stawiamy nast�pny krok (zapalamy piksel)
            }while (CurrentX != X2); // idziemy tak d�ugo, a� osi�gniemy punkt docelowy
        }
        else // w przeciwnym razie idziemy "po igrekach" 
        {
            TwoDyAccumulatedError = 0; 
            do 
            {
                CurrentY += Yinc; 
                TwoDyAccumulatedError += TwoDx;
                if(TwoDyAccumulatedError>Dy) 
                {
                    CurrentX += Xinc;
                    TwoDyAccumulatedError -= TwoDy;
                }
                lcdSetPixel(CurrentX,CurrentY); 
            }while (CurrentY != Y2);
        }
    }
}
